home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 16
/
Aminet 16 (1996)(GTI - Schatztruhe)[!][Dec 1996].iso
/
Aminet
/
comm
/
term
/
term_source.lha
/
Extras
/
Source
/
term-source.lha
/
FileBuffer.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-10-20
|
20KB
|
1,030 lines
/*
** FileBuffer.c
**
** Double-buffered file I/O routines
**
** Copyright © 1990-1996 by Olaf `Olsen' Barthel
** All Rights Reserved
**
** :ts=4
*/
#ifndef _GLOBAL_H
#include "Global.h"
#endif
/* Argument types. */
enum { ARG_NAME,ARG_MODE };
enum { ARG_OFFSET,ARG_ORIGIN };
/* Seek offsets. */
enum { SEEKFILE_SET,SEEKFILE_CURR,SEEKFILE_END };
/* Command codes. */
enum { BUF_CLOSE,BUF_SEEK,BUF_FILL,BUF_FLUSH };
/* OpenFileWithMode(STRPTR Name,STRPTR ModeString):
*
* Open a plain AmigaDOS file and turn the stdio mode string
* into the appropriate Open() mode.
*/
STATIC BPTR
OpenFileWithMode(STRPTR Name,STRPTR ModeString)
{
LONG Mode;
switch(ModeString[0])
{
case 'r':
if(ModeString[1] == '+')
Mode = MODE_READWRITE;
else
Mode = MODE_OLDFILE;
break;
case 'w':
if(ModeString[1] == '+')
{
BPTR SomeLock;
if(SomeLock = Lock(Name,EXCLUSIVE_LOCK))
{
UnLock(SomeLock);
if(!DeleteFile(Name))
return(NULL);
}
Mode = MODE_READWRITE;
}
else
Mode = MODE_NEWFILE;
break;
case 'a':
return(OpenToAppend(Name,NULL));
default:
return(NULL);
}
return(Open(Name,Mode));
}
/* ObtainInfo(struct Buffer *File):
*
* Obtain information on the disk a buffered file
* resides on.
*/
STATIC VOID
ObtainInfo(struct Buffer *File)
{
/* Lock on the path available? */
if(File->InfoPort)
{
/* This is more or less a trick; we try to obtain
* information on a filing system without having a
* shared lock on it. We cannot DupLockFromFH()
* the file handle since it was allocated for
* exclusive access. The ACTION_DISK_INFO packet
* solves this problem as it requires only the
* filing system task (MsgPort) to be given.
*/
if(!DoPkt(File->InfoPort,ACTION_DISK_INFO,MKBADDR(&File->InfoData),0,0,0,0))
{
File->InfoData.id_NumBlocks = 0;
File->InfoData.id_BytesPerBlock = 0;
}
}
}
/* FileBufferServer():
*
* Background process to handle the buffering
* of a filehandle, automatically gets invoked
* when a file is opened.
*/
STATIC VOID SAVE_DS
FileBufferServer(VOID)
{
BOOL Terminated,Done,WasFull;
struct Buffer *Buffer;
struct MsgPort *Port;
struct Process *Me;
UBYTE *String;
LONG Length;
APTR Data;
Terminated = FALSE;
/* Wait for startup message (-> Buffer). */
Me = (struct Process *)FindTask(NULL);
Port = &Me->pr_MsgPort;
WaitPort(Port);
Buffer = (struct Buffer *)GetMsg(Port);
/* Open the file and obtain a filehandle. */
String = (STRPTR)Buffer->ActionData[ARG_MODE];
/* Put the message into the list. */
ObtainSemaphore(&DoubleBufferSemaphore);
AddTail(&DoubleBufferList,(struct Node *)Buffer);
ReleaseSemaphore(&DoubleBufferSemaphore);
/* Remember the opening date. */
DateStamp(&Buffer->OpenDate);
/* Check for the open type. */
Buffer->WriteAccess = (BOOL)(String[0] != 'r');
Buffer->FileHandle = OpenFileWithMode((STRPTR)Buffer->ActionData[ARG_NAME],String);
/* Clear signal bit. */
ClrSignal(SIG_COMMAND);
/* Did the file open? */
if(Buffer->FileHandle)
{
Buffer->Data = Buffer->DataBuffer[0];
Buffer->DataCount = 1;
Buffer->Fresh = TRUE;
/* If not in write mode fill the buffers. */
if(!Buffer->WriteAccess)
{
/* Fill the first one synchronously. */
Buffer->ReadBufFull = Read(Buffer->FileHandle,Buffer->Data,Buffer->BufLength);
Buffer->Read = TRUE;
Buffer->RealPosition = Buffer->ReadBufFull;
/* Restart caller. */
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
/* Fill the second buffe asynchronously. */
Buffer->DataLength[1] = Buffer->Cached = Read(Buffer->FileHandle,Buffer->DataBuffer[1],Buffer->BufLength);
Buffer->RealPosition += Buffer->Cached;
}
else
{
Buffer->InfoPort = ((struct FileHandle *)BADDR(Buffer->FileHandle))->fh_Type;
ObtainInfo(Buffer);
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
}
}
else
Terminated = TRUE;
/* Go into loop waiting for commands. */
while(!Terminated)
{
Wait(SIG_COMMAND);
Done = FALSE;
Buffer->Result = 0;
/* Take care of each action. */
switch(Buffer->Action)
{
/* Close the file, flush any dirty
* buffers and exit.
*/
case BUF_CLOSE:
Buffer->Result = TRUE;
if(Buffer->BufPosition && Buffer->Written)
{
if(Write(Buffer->FileHandle,Buffer->Data,Buffer->BufPosition) != Buffer->BufPosition)
Buffer->Result = FALSE;
}
if(!Close(Buffer->FileHandle))
Buffer->Result = FALSE;
Terminated = TRUE;
break;
/* Seek to a specific file position. */
case BUF_SEEK:
Buffer->Result = 0;
/* Do nothing if buffer is still
* untouched and we are required
* to seek back to the beginning
* of the file.
*/
if(Buffer->Fresh && !Buffer->ActionData[ARG_OFFSET] && Buffer->ActionData[ARG_ORIGIN] == SEEKFILE_SET)
{
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
Done = TRUE;
}
else
{
Buffer->WriteBufFull = Buffer->BufLength;
Buffer->Read = FALSE;
if(Buffer->BufPosition && Buffer->Written)
{
if(Write(Buffer->FileHandle,Buffer->Data,Buffer->BufPosition) != Buffer->BufPosition)
Buffer->Result = -1;
else
ObtainInfo(Buffer);
}
if(!Buffer->Result)
{
Buffer->Result = Buffer->RealPosition - (Buffer->ReadBufFull + Buffer->Cached);
switch(Buffer->ActionData[ARG_ORIGIN])
{
case SEEKFILE_SET:
if(Seek(Buffer->FileHandle,Buffer->ActionData[ARG_OFFSET],OFFSET_BEGINNING) != -1)
Buffer->RealPosition = Buffer->ActionData[ARG_OFFSET];
else
Buffer->Result = -1;
break;
case SEEKFILE_CURR:
if(!Buffer->WriteAccess && Buffer->ActionData[ARG_OFFSET] >= 0 && Buffer->ReadBufFull - Buffer->ActionData[ARG_OFFSET] >= 0)
{
Buffer->ReadBufFull -= Buffer->ActionData[ARG_OFFSET];
Buffer->Data += Buffer->ActionData[ARG_OFFSET];
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
Done = TRUE;
break;
}
if(Seek(Buffer->FileHandle,-(Buffer->ReadBufFull + Buffer->Cached) + Buffer->ActionData[ARG_OFFSET],OFFSET_CURRENT) != -1)
Buffer->RealPosition += -(Buffer->ReadBufFull + Buffer->Cached) + Buffer->ActionData[ARG_OFFSET];
else
Buffer->Result = -1;
break;
case SEEKFILE_END:
if(Seek(Buffer->FileHandle,Buffer->ActionData[ARG_OFFSET],OFFSET_END) != -1)
Buffer->RealPosition = Seek(Buffer->FileHandle,0,OFFSET_CURRENT);
else
Buffer->Result = -1;
break;
default:
Buffer->Result = -1;
}
Buffer->ReadBufFull = 0;
if(Buffer->Result != -1)
{
Buffer->Data = Buffer->DataBuffer[0];
Buffer->DataCount = 1;
if(!Buffer->WriteAccess)
{
Buffer->ReadBufFull = Read(Buffer->FileHandle,Buffer->Data,Buffer->BufLength);
Buffer->WriteBufFull = 0;
Buffer->Read = TRUE;
Buffer->RealPosition += Buffer->ReadBufFull;
if(Buffer->ReadBufFull)
{
Buffer->Cached = Buffer->DataLength[1] = Read(Buffer->FileHandle,Buffer->DataBuffer[1],Buffer->BufLength);
Buffer->RealPosition += Buffer->Cached;
}
}
}
else
Buffer->LastActionFailed = TRUE;
}
else
Buffer->ReadBufFull = 0;
Buffer->BufPosition = 0;
Buffer->Written = FALSE;
}
break;
/* Fill the buffer with fresh data. */
case BUF_FILL:
Buffer->Data = Buffer->DataBuffer[Buffer->DataCount];
Buffer->ReadBufFull = Buffer->DataLength[Buffer->DataCount];
Buffer->WriteBufFull = 0;
Buffer->BufPosition = 0;
Buffer->Read = TRUE;
Buffer->Written = FALSE;
Buffer->Fresh = FALSE;
if(Buffer->ReadBufFull)
WasFull = TRUE;
else
WasFull = FALSE;
/* The buffer contents have been
* swapped, now wake the caller
* up and fill the next buffer
* asynchronously.
*/
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
Done = TRUE;
if(WasFull)
{
Buffer->DataCount = (Buffer->DataCount + 1) % BUFFER_NUMBER;
Buffer->Cached = Buffer->DataLength[Buffer->DataCount] = Read(Buffer->FileHandle,Buffer->DataBuffer[Buffer->DataCount],Buffer->BufLength);
Buffer->RealPosition += Buffer->Cached;
if(!Buffer->DataLength[Buffer->DataCount])
{
if(IoErr())
Buffer->LastActionFailed = TRUE;
}
}
break;
/* Flush the contents of the buffer to disk. */
case BUF_FLUSH:
if(Buffer->BufPosition && Buffer->Written)
{
Data = Buffer->Data;
Length = Buffer->BufPosition;
Buffer->Data = Buffer->DataBuffer[Buffer->DataCount];
Buffer->DataCount = (Buffer->DataCount + 1) % BUFFER_NUMBER;
Buffer->ReadBufFull = 0;
Buffer->WriteBufFull = Buffer->BufLength;
Buffer->BufPosition = 0;
Buffer->Read = FALSE;
Buffer->Written = FALSE;
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
Done = TRUE;
if(Write(Buffer->FileHandle,Data,Length) != Length)
Buffer->LastActionFailed = TRUE;
else
{
ObtainInfo(Buffer);
Buffer->RealPosition += Length;
}
}
else
{
Buffer->ReadBufFull = 0;
Buffer->WriteBufFull = Buffer->BufLength;
Buffer->BufPosition = 0;
Buffer->Read = FALSE;
Buffer->Written = FALSE;
}
Buffer->Fresh = FALSE;
break;
}
/* Ring back if necessary. */
if(!Done && !Terminated)
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
}
/* Remove the message from the list. */
ObtainSemaphore(&DoubleBufferSemaphore);
Remove((struct Node *)Buffer);
ReleaseSemaphore(&DoubleBufferSemaphore);
/* Lock & quit. */
Forbid();
Signal((struct Task *)Buffer->Caller,SIG_HANDSHAKE);
}
/* BufferFill(struct Buffer *Buffer):
*
* Fills a given buffer with fresh data.
*/
STATIC BOOL
BufferFill(struct Buffer *Buffer)
{
if(Buffer->LastActionFailed)
return(FALSE);
else
{
if(!Buffer->ReadBufFull)
{
Buffer->Action = BUF_FILL;
ShakeHands((struct Task *)Buffer->Child,SIG_COMMAND);
}
return(TRUE);
}
}
/* IsValidBuffer(struct Buffer *Buffer):
*
* Scans the double buffered file list for
* a valid entry.
*/
STATIC BOOL
IsValidBuffer(struct Buffer *Buffer)
{
struct Node *Node;
BOOL GotIt;
ObtainSemaphore(&DoubleBufferSemaphore);
GotIt = FALSE;
for(Node = DoubleBufferList.lh_Head ; Node->ln_Succ ; Node = Node->ln_Succ)
{
if(Buffer == (struct Buffer *)Node)
{
GotIt = TRUE;
break;
}
}
ReleaseSemaphore(&DoubleBufferSemaphore);
return(GotIt);
}
/* OpenFileSimple(STRPTR Name,STRPTR AccessMode):
*
* Open simple (unbuffered) file.
*/
STATIC struct Buffer *
OpenFileSimple(STRPTR Name,STRPTR AccessMode)
{
struct Buffer *Buffer;
/* Allocate buffer handle (dummy). */
if(Buffer = (struct Buffer *)AllocVecPooled(sizeof(struct Buffer),MEMF_ANY | MEMF_CLEAR))
{
/* Provide basic information. */
DateStamp(&Buffer->OpenDate);
Buffer->WriteAccess = (BOOL)(AccessMode[0] != 'r');
Buffer->SimpleIO = TRUE;
Buffer->FileHandle = OpenFileWithMode(Name,AccessMode);
/* Did we succeed in opening the file? */
if(Buffer->FileHandle)
{
/* To keep track of the free space still
* available on the filing system.
*/
if(Buffer->WriteAccess)
{
Buffer->InfoPort = ((struct FileHandle *)BADDR(Buffer->FileHandle))->fh_Type;
ObtainInfo(Buffer);
}
/* Link the file into the list. */
ObtainSemaphore(&DoubleBufferSemaphore);
AddTail(&DoubleBufferList,(struct Node *)Buffer);
ReleaseSemaphore(&DoubleBufferSemaphore);
return(Buffer);
}
else
FreeVecPooled(Buffer);
}
return(NULL);
}
/* OpenFileBuffered(STRPTR Name,STRPTR AccessMode):
*
* Open double-buffered file.
*/
STATIC struct Buffer *
OpenFileBuffered(STRPTR Name,STRPTR AccessMode)
{
struct Buffer *Buffer;
LONG Size;
Size = (Config->MiscConfig->IOBufferSize + 15) & ~15;
if(Size < 2048)
Size = 2048;
/* Allocate the buffer data. */
do
{
Buffer = (struct Buffer *)AllocVecPooled(sizeof(struct Buffer) + ((Size + 15) & ~15) * BUFFER_NUMBER + 15,MEMF_ANY | MEMF_CLEAR);
Size /= 2;
}
while(!Buffer && Size > 2048);
if(Buffer)
{
struct Process *Process;
LONG i;
/* Set up the first buffer. */
Buffer->DataBuffer[0] = (UBYTE *)(((ULONG)(Buffer + 1) + 15) & ~15);
/* Set up the individual buffers. */
for(i = 1 ; i < BUFFER_NUMBER ; i++)
Buffer->DataBuffer[i] = Buffer->DataBuffer[i - 1] + Size;
Buffer->BufLength = Size;
Buffer->WriteBufFull = Buffer->BufLength;
/* Create the asynchronous file server. */
if(!(Process = CreateNewProcTags(
NP_Entry, FileBufferServer,
NP_Name, "term File Process",
NP_WindowPtr, -1,
TAG_DONE)))
{
FreeVecPooled(Buffer);
return(NULL);
}
/* Set up the message header. */
Buffer->Message.mn_Length = sizeof(struct Buffer);
Buffer->ActionData[ARG_NAME] = (LONG)Name;
Buffer->ActionData[ARG_MODE] = (LONG)AccessMode;
Buffer->Child = Process;
Buffer->Caller = (struct Process *)FindTask(NULL);
Forbid();
/* Send it to the waiting server process. */
PutMsg(&Process->pr_MsgPort,&Buffer->Message);
/* Wait for ringback. */
WaitForHandshake();
Permit();
/* Do we have a valid filehandle? */
if(!Buffer->FileHandle)
{
FreeVecPooled(Buffer);
return(NULL);
}
else
return(Buffer);
}
return(NULL);
}
/* BPrintf():
*
* Prints text into a buffered file.
*/
LONG
BPrintf(struct Buffer *Buffer,STRPTR Format,...)
{
UBYTE String[256];
va_list VarArgs;
va_start(VarArgs,Format);
LimitedVSPrintf(sizeof(String),String,Format,VarArgs);
va_end(VarArgs);
return(BufferWrite(Buffer,String,strlen(String)));
}
/* BufferFlush(struct Buffer *Buffer):
*
* Flush the contents of a given buffer to disk.
*/
BOOL
BufferFlush(struct Buffer *Buffer)
{
if(Buffer->LastActionFailed)
return(FALSE);
else
{
if(Buffer->BufPosition && Buffer->Written)
{
Buffer->Action = BUF_FLUSH;
ShakeHands((struct Task *)Buffer->Child,SIG_COMMAND);
}
return(TRUE);
}
}
/* BufferClose(struct Buffer *Buffer):
*
* Close a buffered filehandle.
*/
BOOL
BufferClose(struct Buffer *Buffer)
{
if(IsValidBuffer(Buffer))
{
BOOL Success;
/* Unbuffered file handle? */
if(Buffer->SimpleIO)
{
/* Close the file. */
if(Close(Buffer->FileHandle))
Success = TRUE;
else
Success = FALSE;
/* Unlink the handle. */
ObtainSemaphore(&DoubleBufferSemaphore);
Remove((struct Node *)Buffer);
ReleaseSemaphore(&DoubleBufferSemaphore);
}
else
{
Buffer->Action = BUF_CLOSE;
ShakeHands((struct Task *)Buffer->Child,SIG_COMMAND);
Success = Buffer->Result;
}
FreeVecPooled(Buffer);
return(Success);
}
else
return(FALSE);
}
/* BufferOpen(STRPTR Name,STRPTR AccessMode):
*
* Open a file for buffered I/O.
*/
struct Buffer *
BufferOpen(STRPTR Name,STRPTR AccessMode)
{
/* Simple file handling? */
if(Config->MiscConfig->SimpleIO)
return(OpenFileSimple(Name,AccessMode));
else
{
struct Buffer *Buffer;
/* Try to open a buffered file, if unsuccessful
* fall back to simple file I/O.
*/
if(!(Buffer = OpenFileBuffered(Name,AccessMode)))
Buffer = OpenFileSimple(Name,AccessMode);
return(Buffer);
}
}
/* BufferSeek(struct Buffer *Buffer,LONG Offset,LONG Origin):
*
* Move the read/write pointer to a specific position
* in a file (not really buffered).
*/
BOOL
BufferSeek(struct Buffer *Buffer,LONG Offset,LONG Origin)
{
if(Buffer->SimpleIO)
{
if(Seek(Buffer->FileHandle,Offset,Origin) == -1)
return(FALSE);
else
return(TRUE);
}
else
{
Buffer->Action = BUF_SEEK;
Buffer->ActionData[ARG_OFFSET] = Offset;
Buffer->ActionData[ARG_ORIGIN] = Origin;
ShakeHands((struct Task *)Buffer->Child,SIG_COMMAND);
if(Buffer->Result == -1)
return(FALSE);
else
return(TRUE);
}
}
/* BufferRead():
*
* Read data from a file (buffered).
*/
LONG
BufferRead(struct Buffer *Buffer,STRPTR Destination,LONG Size)
{
if(Buffer->SimpleIO)
{
LONG Bytes;
Buffer->Used = TRUE;
if((Bytes = Read(Buffer->FileHandle,Destination,Size)) < 0)
Bytes = 0;
return(Bytes);
}
else
{
LONG BytesRead,ToCopy,BufPosition,ReadBufFull;
UBYTE *Data;
BytesRead = 0;
/* If there is still data to be written in
* the buffer, write it.
*/
if(Buffer->Written)
{
if(!BufferFlush(Buffer))
return(0);
}
/* Set up for read access. */
BufPosition = Buffer->BufPosition;
ReadBufFull = Buffer->ReadBufFull;
Data = &Buffer->Data[BufPosition];
/* Remember access. */
Buffer->Used = TRUE;
/* Continue until all data has been processed. */
while(Size)
{
/* Determine number of bytes to transfer. */
if(ToCopy = (Size > ReadBufFull) ? ReadBufFull : Size)
{
CopyMem(Data,Destination,ToCopy);
Size -= ToCopy;
BufPosition += ToCopy;
ReadBufFull -= ToCopy;
Destination += ToCopy;
Data += ToCopy;
BytesRead += ToCopy;
}
else
{
/* Refill buffer with data. */
Buffer->BufPosition = BufPosition;
Buffer->ReadBufFull = ReadBufFull;
if(!BufferFill(Buffer))
return(BytesRead);
if(!Buffer->ReadBufFull)
{
Buffer->BufPosition = BufPosition;
return(BytesRead);
}
/* Pick up new data. */
BufPosition = Buffer->BufPosition;
ReadBufFull = Buffer->ReadBufFull;
Data = Buffer->Data;
}
}
/* Install new data. */
Buffer->BufPosition = BufPosition;
Buffer->ReadBufFull = ReadBufFull;
return(BytesRead);
}
}
/* BufferWrite():
*
* Write data to a file (buffered).
*/
LONG
BufferWrite(struct Buffer *Buffer,STRPTR Source,LONG Size)
{
if(Buffer->SimpleIO)
{
LONG Bytes;
Buffer->Used = TRUE;
Buffer->WriteAccess = TRUE;
if((Bytes = Write(Buffer->FileHandle,Source,Size)) < 0)
Bytes = 0;
else
{
Buffer->BufPosition += Bytes;
if(Buffer->BufPosition >= Config->MiscConfig->IOBufferSize)
{
Buffer->BufPosition = 0;
ObtainInfo(Buffer);
}
}
return(Bytes);
}
else
{
LONG BytesWritten,ToCopy,BufPosition,WriteBufFull;
UBYTE *Data;
BytesWritten = 0;
/* If there is still read data in the buffer,
* reset the control information.
*/
if(Buffer->Read)
{
Buffer->WriteBufFull = Buffer->BufLength;
Buffer->BufPosition = 0;
Buffer->Read = FALSE;
}
/* Set up for write access. */
Buffer->Written = TRUE;
BufPosition = Buffer->BufPosition;
WriteBufFull = Buffer->WriteBufFull;
Data = &Buffer->Data[BufPosition];
/* Remember access. */
Buffer->Used = TRUE;
/* Continue until all data has been processed. */
while(Size)
{
/* Determine number of bytes to transfer. */
if(ToCopy = (Size > WriteBufFull ? WriteBufFull : Size))
{
CopyMem(Source,Data,ToCopy);
Size -= ToCopy;
BufPosition += ToCopy;
WriteBufFull -= ToCopy;
Source += ToCopy;
Data += ToCopy;
BytesWritten += ToCopy;
}
else
{
/* Flush the contents of the
* write buffer.
*/
Buffer->BufPosition = BufPosition;
Buffer->WriteBufFull = WriteBufFull;
if(!BufferFlush(Buffer))
return(BytesWritten);
/* Pick up new data. */
BufPosition = Buffer->BufPosition;
WriteBufFull = Buffer->WriteBufFull;
Data = Buffer->Data;
/* Important - or BufferFlush() won't
* write the final buffer contents when
* the buffered file handle is freed up.
*/
Buffer->Written = TRUE;
}
}
/* Install new data. */
Buffer->BufPosition = BufPosition;
Buffer->WriteBufFull = WriteBufFull;
return(BytesWritten);
}
}